﻿using System;
using System.Collections.Generic;
using System.Linq;
using MyFloridaHouse.DataAccess;
using MyFloridaHouse.DataAccess.DTO;
using MyFloridaHouse.libSTO;
using MyFloridaHouse.libSvCommonUtilities;
using MyFloridaHouse.libSvDTO;
using Microsoft.SqlServer.Types;
using System.Data.SqlTypes;
using MyFloridaHouse.DataAccess.SQLServerTypes;
using MyFloridaHouse.libSvUtilities;

namespace MyFloridaHouse.libServiceBusinessRules
{
    public static class BoundingBoxRegions
    {

        public static RegionShapesInViewSTO GetRegionShapesInView(RegionShapesInViewSTO sto)
        {

            try
            {
                List<int> regionIDList = GetShapeRegionIDList(sto.layerID, sto.north, sto.east, sto.south, sto.west);

                if (0 == regionIDList.Count())
                    {
                        //TODO: Change this string to constant reference, it's used in PDF_DAL.cs
                        sto.errorMessage = "No shapes found";
                        return sto;
                    }
                    else
                    {

                        RegionsShapesSTO result = BoundingBoxRegions.GetRegionListShapesClipped(sto.zoomLevel, sto.layerID, regionIDList, sto.north, sto.east, sto.south, sto.west);
                    if (0 != result.errorMessage.Length)
                    {
                        sto.errorMessage = result.errorMessage;
                    }
                    else
                    {
                        sto.compressedRegionList = result.compressedRegionList;
                    }
                }
            }

            catch (Exception ex)
            {
                sto.errorMessage = ex.Message;
                return sto;
            }
            return sto;
        }


        public static RegionIDsSTO GetRegionIDsInView(int targetLayerID, double iNorth, double iEast, double iSouth, double iWest)
        {
            RegionIDsSTO results = new RegionIDsSTO();
            results.layerID = targetLayerID;
            try
            {
                List<int> shapeIDList = GetShapeRegionIDList(targetLayerID, iNorth, iEast, iSouth, iWest);
            
                byte[] serializedList = Serializer.Serialize(shapeIDList);
                results.compressedRegionIDList = CompressionManager.Compress(serializedList);
            }
            
            catch (Exception ex)
            {
                results.errorMessage = ex.Message;
                return results;
            }
            return results;
        }

        private static List<int> GetShapeRegionIDList(int targetLayerID, double iNorth, double iEast, double iSouth, double iWest)
        {
            MapMultiShapeDTO boundingBox = BoundingBoxRegions.BuildBoundingBox(iNorth, iEast, iSouth, iWest);

            DAL_CensusData dal = new DAL_CensusData();
            var queryResults = dal.GetShapeRegionIDs(targetLayerID, boundingBox);
            var x = from q in queryResults
                    select q;
            List<int> shapeIDList = x.ToList();
            return shapeIDList;
        }


        public static RegionsShapesSTO GetRegionListShapes(double zoomLevel, int iTargetLayerID, List<int> iRegionIDList)
        {

            RegionsShapesSTO results = new RegionsShapesSTO();
            results.layerID = iTargetLayerID;
            try
            {
                DAL_CensusData dal = new DAL_CensusData();
                List<SQLRegionDTO> regionShapeList = dal.GetRegionListShapes(zoomLevel, iTargetLayerID, iRegionIDList);


                List<RegionDTO> regionsList = ConvertSQLRegionDTOListToRegionDTO(regionShapeList);

                if (regionsList.Count() > 0)
                {
                    regionsList = GetRegionListNames(iTargetLayerID, iRegionIDList, dal, regionsList);
                }
                byte[] serializedList = Serializer.Serialize(regionsList);
                results.compressedRegionList = CompressionManager.Compress(serializedList);
                return results;
            }
            catch (Exception ex)
            {
                results.errorMessage = ex.Message;
                return results;
            }
        }


        public static RegionsShapesSTO GetRegionListShapesClipped(double zoomLevel, int iTargetLayerID, List<int> iRegionIDList, double north, double east, double south, double west)
        {

            RegionsShapesSTO results = new RegionsShapesSTO();
            results.layerID = iTargetLayerID;
            try
            {
                DAL_CensusData dal = new DAL_CensusData();
                List<SQLRegionDTO> regionShapeList = dal.GetRegionListShapes(zoomLevel, iTargetLayerID, iRegionIDList);

                foreach (var rgn in regionShapeList)
                {

                    // clip the shape with the viewing rectangle
                    SqlGeometry clipRect = TOOLS_SQLGeometry.BuildBoxFromCorners(north, south, east, west);
                    clipRect.STSrid = rgn.mapGeom.STSrid;
                    // replace the shape with a clipped shape
                    rgn.mapGeom = rgn.mapGeom.STIntersection(clipRect);

                    // determine the details that changed when we clipped the shape
                    PointDTO centerPoint = new PointDTO();
                    try
                    {
                        PointDTO newCenterPoint = TOOLS_SQLGeometry.GetLabelLocation(rgn.mapGeom);
                        centerPoint = newCenterPoint;
                    }
                    catch
                    {
                        try
                        {
                            // no center found (maybe it isn't a polygon); just grab any old point on the shape
                            SqlGeometry newCenter = rgn.mapGeom.STPointOnSurface();
                            centerPoint = new PointDTO((double)newCenter.STPointN(1).STY, (double)newCenter.STPointN(1).STX);
                        }
                        catch
                        {
                            // OK, things are really wacky, just use whatever label point we already had.
                            centerPoint = new PointDTO(rgn.centerLatY, rgn.centerLngX);
                        }

                    }
                    rgn.centerLatY = centerPoint.latitude;
                    rgn.centerLngX = centerPoint.longitude;
                    rgn.boundingBox = TOOLS_SQLGeometry.GeometrySTEnvelopeToBox(rgn.mapGeom.MakeValid().STEnvelope());
                }

                List<RegionDTO> regionsList = ConvertSQLRegionDTOListToRegionDTO(regionShapeList);
                if (regionsList.Count() > 0)
                {
                    regionsList = GetRegionListNames(iTargetLayerID, iRegionIDList, dal, regionsList);
                }

                byte[] serializedList = Serializer.Serialize(regionsList);
                results.compressedRegionList = CompressionManager.Compress(serializedList);
                return results;
            }
            catch (Exception ex)
            {
                results.errorMessage = ex.Message;
                return results;
            }
        }

        private static List<RegionDTO> ConvertSQLRegionDTOListToRegionDTO(List<SQLRegionDTO> regionShapeList)
        {
            List<RegionDTO> regionsList =
                regionShapeList.ConvertAll(rgn => new RegionDTO
                {
                    regionID = rgn.regionID,
                    centerLatY = rgn.centerLatY,
                    centerLngX = rgn.centerLngX,
                    boundingBox = TOOLS_SQLGeometry.GeometrySTEnvelopeToBox(rgn.mapGeom.MakeValid().STEnvelope()),
                    mapMultiShapeDTO = TOOLS_SQLGeometry.ToMapMultiShapeDTO(rgn.mapGeom)
                });
            return regionsList;
        }
        private static List<RegionDTO> GetRegionListNames(int iTargetLayerID, List<int> iRegionIDList, DAL_CensusData dal, List<RegionDTO> regionsList)
        {
            // if we found any regions, we need to retrieve the names of those regions
            // get the nameDictionaryID from the global applayersTable
            int nameDictionaryID = DAL_Global.GetDisplayDictionaryIDFromLayerID(iTargetLayerID);
            if (nameDictionaryID >= 0)
            {
                // a dictionaryID is defined for the name, so go get it 
                var regionsNameList = dal.GetRegionsListName(iTargetLayerID, iRegionIDList, nameDictionaryID);

                if (regionsNameList.Count > 0)
                {
                    // Join the region's shape with the region's name for each region returned
                    // however, some regions may not have name data, so need to do a join that gives us all regions back
                    regionsList = (from regionShape in regionsList
                                   join regionNames in regionsNameList
                                   on regionShape.regionID equals regionNames.regionID into tempRegions
                                   from subRegionName in tempRegions.DefaultIfEmpty()
                                   select new RegionDTO
                                   {
                                       regionID = regionShape.regionID,
                                       name = ((subRegionName == null)  ? string.Empty : subRegionName.dataValue),
                                       centerLngX = regionShape.centerLngX,
                                       centerLatY = regionShape.centerLatY,
                                       mapMultiShapeDTO = regionShape.mapMultiShapeDTO,
                                       boundingBox = regionShape.boundingBox
                                   }).ToList();
                }
            }

            // TODO:  Fix regionsList linq query in code above to return name as empty string if no data value is found, then remove loop below that assures rgn.name is not null
            var nullNameQuery = from rgn in regionsList
                                where rgn.name == null
                                select rgn;
            foreach (var rgn in nullNameQuery)
            {
                rgn.name = string.Empty;
            }
            return regionsList;
        }

        public static LayerRegionsDataSTO GetRegionListData(int iTargetLayerID, List<int> iRegionIDList, string iSourceLayerIDList, string iDictionaryIDList)
        {
            LayerRegionsDataSTO results = new LayerRegionsDataSTO();
            try
            {
                DAL_CensusData dal = new DAL_CensusData();
                var queryResults2 = dal.GetRegionListData(iTargetLayerID, iRegionIDList, iSourceLayerIDList, iDictionaryIDList);
                var z = from q in queryResults2
                        select q;
                results.regionsDataList = z.ToList();
                results.layerID = iTargetLayerID;
                return results;
            }
            catch (Exception ex)
            {
                results.errorMessage = ex.Message;
                return results;
            }
        }


        public static MapMultiShapeDTO BuildBoundingBox(double iNorth, double iEast, double iSouth, double iWest)
        {
            MapMultiShapeDTO polygon = new MapMultiShapeDTO();
            polygon.shapeType = MapMultiShapeType.Polygon;
            polygon.shapeList = new List<MapMultiShapeEntryDTO>();
            MapMultiShapeEntryDTO poly = new MapMultiShapeEntryDTO();
            poly.ExteriorRing = new List<PointDTO>();
            poly.ExteriorRing.Add(new PointDTO(iSouth, iEast));
            poly.ExteriorRing.Add(new PointDTO(iNorth, iEast));
            poly.ExteriorRing.Add(new PointDTO(iNorth, iWest));
            poly.ExteriorRing.Add(new PointDTO(iSouth, iWest));
            poly.ExteriorRing.Add(new PointDTO(iSouth, iEast));
            poly.InteriorRings = new List<List<PointDTO>>();
            polygon.shapeList.Add(poly);
            return polygon;
        }


    }
}

